package com.asset.dispoal.file.evidenceRecognition.service.commom;

import com.asset.dispoal.file.evidenceRecognition.domain.db.entity.EvidenceFileDto;
import com.asset.dispoal.file.evidenceRecognition.dto.EvidenceRecognitionRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import tech.mhuang.pacebox.core.ddd.BaseCommand;
import tech.mhuang.pacebox.core.exception.BusinessException;
import tech.mhuang.pacebox.core.file.FileUtil;
import tech.mhuang.pacebox.core.id.BaseIdeable;
import tech.mhuang.pacebox.core.io.IOUtil;
import tech.mhuang.pacebox.core.util.StringUtil;
import tech.mhuang.pacebox.springboot.protocol.Result;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;


/**
 * @author qr
 * @date 2021/11/23$
 */
@Component
@Slf4j
public class EvidenceUploadCommand implements BaseCommand<EvidenceFileDto, Result> {

    @Autowired
    BaseIdeable<String> snowFlake;

    @Autowired
    private Environment environment;

    @Autowired
    FileEvidenceRecognitionCommand fileEvidenceRecognitionCommand;

    @Override
    public Result execute(EvidenceFileDto evidenceFileDto) {
        LocalDateTime now = LocalDateTime.now();
        String year = now.getYear() + "";
        String month = now.getMonthValue() + "";
        String day = now.getDayOfMonth() + "";
        String type = "CONDITION";
        String baseDir = getBaseDir(type) + "source/" + year + "/" + month + "/" + day + "/";
        File baseDirFile = new File(baseDir);
        if (!baseDirFile.exists()) {
            baseDirFile.mkdirs();
        }

        if (evidenceFileDto.getTaskId() == null) {
            evidenceFileDto.setTaskId(snowFlake.generateId());
        }
        try {
            singleUpload(evidenceFileDto.getEvidenceRecognitionRequest(), evidenceFileDto.getChunk(), evidenceFileDto.getChunks(), evidenceFileDto.getTaskId(), evidenceFileDto.getUuid(), evidenceFileDto.getName(), baseDir, evidenceFileDto.getFile());

        } catch (Exception e) {
            throw new BusinessException(Result.SYS_FAILD, "上传失败了", e);
        }
        return Result.ok();

    }


    private String getBaseDir(String type) {
        String path = null;
        try {
            if (type != null) {
                String dir = environment.getProperty("file.evidence.url");
                File baseDir = new File(dir);
                if (!baseDir.exists()) {
                    baseDir.mkdirs();
                }
                path = dir;
            }
        } catch (Exception e) {
            throw new BusinessException(500, "上传类型错误");
        }
        return path;
    }


    private void singleUpload(EvidenceRecognitionRequest evidenceRecognitionRequest, String chunk, String chunks, String task_id, String uuid, String name, String baseDir, MultipartFile file) throws IOException {
        // 分片文件存放包路径
        String targetDir = baseDir + name.split("\\.")[0] + uuid;
        // 分片文件包路径+文件名+文件类型
        String targetFile = baseDir + uuid + "-" + task_id + name;
        // 分片文件对象
        File chunkFile;
        // 总块数
        int chunksIntLabel = 0;
        // 当前块索引
        int chunkIntLabel = 0;
        if (chunks != null) {
            chunksIntLabel = Integer.parseInt(chunks);
        }
        if (chunk != null) {
            chunkIntLabel = Integer.parseInt(chunk);
        }
        boolean isSuccess = false;
        if (task_id != null) {
            // 总块数大于0
            if (chunksIntLabel > 0) {
                File dir = new File(targetDir);
                // 文件包路径创建
                if (!dir.exists()) {
                    dir.mkdir();
                }
                String chunkFileName = task_id + "&&&&&" + chunkIntLabel;
                chunkFile = new File(targetDir, chunkFileName);
            } else {
                chunkFile = new File(targetFile);
            }
            FileOutputStream fs = new FileOutputStream(chunkFile);
            IOUtil.copy(file.getInputStream(), fs, true);
            if (chunksIntLabel > 0) {
                if (mergeFile(targetDir, targetFile, chunksIntLabel)) {
                    isSuccess = true;
                }
            } else {
                isSuccess = true;
            }
        } else {
            return;
        }
        if (isSuccess) {
            fileUpload(file, evidenceRecognitionRequest, baseDir, uuid + "-" + task_id + name, name);
        }
    }

    private boolean mergeFile(String fileDir, String fileName, Integer chunks) {
        if (isAllUploaded(fileDir, chunks)) {
            try {
                List<FileInputStream> targetList = new CopyOnWriteArrayList<>();
                File parentFile = new File(fileDir);
                File[] files = parentFile.listFiles();
                for (int i = 0; i < chunks; i++) {
                    for (File file : files) {
                        if (file.getName().contains("&&&&&" + i)) {
                            FileInputStream sourceStream = new FileInputStream(file);
                            targetList.add(sourceStream);
                            break;
                        }
                    }
                }

                Enumeration<FileInputStream> targetEnum = Collections.enumeration(targetList);
                SequenceInputStream sequenceStream = new SequenceInputStream(targetEnum);
                // 合分片文件
                saveStreamToFile(sequenceStream, fileName);

                for (File file : files) {
                    if (file.isDirectory()) {
                        FileUtil.deleteDirectory(file);
                    } else {
                        FileUtil.delete(file);
                    }
                }
                FileUtil.deleteDirectory(parentFile);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage());
                return false;
            }
        }
        return false;
    }


    private void saveStreamToFile(SequenceInputStream inputStream, String filePath) throws Exception {
        /* 创建输出流，写入数据，合并分块 */
        OutputStream outputStream = Files.newOutputStream(Paths.get(filePath));
        IOUtil.copy(inputStream, outputStream, true);
    }

    private Boolean isAllUploaded(String fileDir, int chunk) {
        File file = new File(fileDir);
        File[] files = file.listFiles();
        int fileLength = files.length;
        return fileLength == chunk;
    }


    public void fileUpload(MultipartFile file, EvidenceRecognitionRequest evidenceRecognitionRequest, String baseSourceUrl, String sourceFileName, String name) {
        LocalDate now = LocalDate.now();
        String year = String.valueOf(now.getYear());
        String month = String.valueOf(now.getMonthValue());
        String day = String.valueOf(now.getDayOfMonth());
        String generateId = snowFlake.generateId();
        //系统定义上传文件解析后存放的路径
        String sysUploadUrl = environment.getProperty("file.evidence.url");

        String filePath = evidenceRecognitionRequest.getEvidenceTypeId() + "/" + evidenceRecognitionRequest.getAssetInstBatchId() + "/";
        String resolveUrl = "/" + year + "/" + month + "/" + day + "/" + filePath;
        String baseResolveUrl = sysUploadUrl + resolveUrl;
        try {
            //保存源文件的上传路径以及原名称
            if (name.matches(".*.zip")) {
                log.info(name + "----是压缩文件夹-------");
                unZipFile(baseSourceUrl, baseResolveUrl, sourceFileName, evidenceRecognitionRequest, resolveUrl);

            } else if (name.matches(".*.rar")) {
                log.info(name + "----是压缩文件夹-------");
                throw new BusinessException(500, "暂不支持rar格式传输");
            } else {
                if (!new File(baseResolveUrl).exists()) {
                    FileUtil.createDirectory(baseResolveUrl, true);
                }
                String replaceFileName = generateId + "_" + name;
                FileOutputStream resolveFs = new FileOutputStream(baseResolveUrl + replaceFileName);
                IOUtil.copy(file.getInputStream(), resolveFs, true);
                evidenceRecognitionRequest.setFilepath(baseResolveUrl);
                evidenceRecognitionRequest.setReplaceFileName(replaceFileName);
                evidenceRecognitionRequest.setOriginalName(name);
                fileEvidenceRecognitionCommand.execute(evidenceRecognitionRequest);
            }
        } catch (IOException e) {
            log.error("保存文件异常", e);
            throw new BusinessException(500, e.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(500, e.getMessage(), e);
        }
    }

    private void unZipFile(String filePath1, String outPath, String fileName, EvidenceRecognitionRequest evidenceRecognitionRequest, String resolveUrl) {
        String filePath = filePath1 + fileName;
        // 压缩文件的实列,并设置编码
        ZipFile zipFile;
        try {
            zipFile = new ZipFile(filePath, "GBK");
            File file;
            // 获取压缩文中的所有项
            String generateId = snowFlake.generateId();
            for (Enumeration<ZipEntry> enumeration = zipFile.getEntries(); enumeration.hasMoreElements(); ) {
                // 获取元素
                ZipEntry zipEntry = enumeration.nextElement();
                // 排除空文件夹
                if (!zipEntry.getName().endsWith(File.separator)) {
                    String filename = zipEntry.getName();
                    boolean ismkdir = filename.lastIndexOf("/") != -1;
                    // 检查此文件是否带有文件夹
                    if (zipEntry.isDirectory()) {
                        // 如果是文件夹则跳过
                        continue;
                    }
                    //源文件名
                    String orinionName = zipEntry.getName().substring(zipEntry.getName().lastIndexOf("/") + 1);
                    String suffixId = snowFlake.generateId();
                    String replaceFileName = (outPath + "/" + generateId + "_" + zipEntry.getName().replace(orinionName, suffixId + "&" + orinionName)).replace("#", "");
                    file = new File(replaceFileName);
                    if (!file.exists()) {
                        // 如果是目录先创建
                        if (ismkdir) {
                            //目录先创建
                            new File(replaceFileName.substring(0, replaceFileName.lastIndexOf("/"))).mkdirs();
                        }
                        File parentDir = file.getParentFile();
                        if (!file.exists()) {
                            parentDir.mkdirs();
                        }
                    }

                    //创建解压后的文件
                    OutputStream os = Files.newOutputStream(Paths.get(replaceFileName));
                    //带缓的写出流
                    BufferedOutputStream bos = new BufferedOutputStream(os);
                    //读取元素
                    InputStream is = zipFile.getInputStream(zipEntry);
                    //读取流的缓存流
                    BufferedInputStream bis = new BufferedInputStream(is);
                    //检查读取流，采用CRC32算法，保证文件的一致性
                    CheckedInputStream cos = new CheckedInputStream(bis, new CRC32());
                    //字节数组，每次读取1024个字节
                    byte[] b = new byte[1024];
                    // 循环读取压缩文件的值
                    while (cos.read(b) != -1) {
                        // 写入到新文件
                        bos.write(b);
                    }
                    cos.close();
                    bis.close();
                    is.close();
                    bos.close();
                    os.close();
                    String substring = replaceFileName.substring(replaceFileName.lastIndexOf("/") + 1);
                    evidenceRecognitionRequest.setParentFilePath(zipEntry.getName().replace(orinionName, ""));
                    if (StringUtil.isEmpty(evidenceRecognitionRequest.getParentFilePath())) {
                        evidenceRecognitionRequest.setFilepath(resolveUrl);
                    } else {
                        evidenceRecognitionRequest.setFilepath(resolveUrl + "/" + generateId + "_" + zipEntry.getName().replace(orinionName, ""));
                    }
                    evidenceRecognitionRequest.setOriginalName(orinionName);
                    evidenceRecognitionRequest.setReplaceFileName(substring);
                    fileEvidenceRecognitionCommand.execute(evidenceRecognitionRequest);
                } else {
                    // 如果为空文件夹，则创建该文件夹
                    new File(outPath + zipEntry.getName()).mkdirs();
                }
                log.info("正在解压文件:" + zipEntry.getName());
                log.info(String.valueOf(zipEntry.getName().matches(".*.zip")));
                if (zipEntry.getName().matches(".*.zip")) {
                    log.info(zipEntry.getName() + "是压缩文件-------------------");
                    unZipFile(outPath, outPath, zipEntry.getName(), evidenceRecognitionRequest, resolveUrl);
                }
            }
            log.info("解压完成");
            zipFile.close();
        } catch (Exception e) {
            throw new BusinessException(Result.SYS_FAILD, "解压文件失败", e);
        }
    }
}
